
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    app_azure_rtos.c
  * @author  MCD Application Team
  * @brief   azure_rtos application implementation file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/

#include "app_azure_rtos.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "bsp_uart.h"
#include "dwt.h"
#include "elog.h"
#include "md0280.h"
#include "systemwatch.h"
#include "tx_api.h"
#include "main.h"
#include "DTH11.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
static uint8_t com_buf[2][30];
static UART_Device *com=NULL;
void sbus_callback(uint8_t *buf, uint16_t len);
void send_temp_humi_uart(float temp, float humi);
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN TX_Pool_Buffer */
/* USER CODE END TX_Pool_Buffer */
static UCHAR tx_byte_pool_buffer[TX_APP_MEM_POOL_SIZE];
static TX_BYTE_POOL tx_app_byte_pool;

/* USER CODE BEGIN PV */
static TX_THREAD demoTaskHandle;
static DTH11_Data dth11_data;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
static void demo_Task(ULONG thread_input);
static int last_temp_int = -100, last_temp_dec = -100;
static int last_humi_int = -100, last_humi_dec = -100;

static void show_temp_humi_on_md0280(void)
{
    
    uint16_t temp_color = ATK_MD0280_YELLOW;
    uint16_t humi_color = ATK_MD0280_YELLOW;

    if(DTH11_Read(&dth11_data) == 0) {
        float temp = dth11_data.temperature_int + dth11_data.temperature_dec / 10.0f;
        float humi = dth11_data.humidity_int + dth11_data.humidity_dec / 10.0f;

        // 判断温度颜色
        if(temp < 20.0f)
            temp_color = ATK_MD0280_BLUE;
        else if(temp > 30.0f)
            temp_color = ATK_MD0280_RED;
        else
            temp_color = ATK_MD0280_YELLOW;

        // 判断湿度颜色
        if(humi < 40.0f)
            humi_color = ATK_MD0280_BLUE;
        else if(humi > 70.0f)
            humi_color = ATK_MD0280_RED;
        else
            humi_color = ATK_MD0280_YELLOW;

        if (dth11_data.temperature_int != last_temp_int || dth11_data.temperature_dec != last_temp_dec) {
            atk_md0280_fill(90, 10, 90 + 5 * 24, 10 + 24, ATK_MD0280_WHITE);
            atk_md0280_show_num(90, 10, dth11_data.temperature_int, 2, ATK_MD0280_LCD_FONT_24, temp_color);
            atk_md0280_show_char(90 + 2 * 24, 10, '.', ATK_MD0280_LCD_FONT_24, temp_color);
            atk_md0280_show_num(90 + 3 * 24, 10, dth11_data.temperature_dec, 1, ATK_MD0280_LCD_FONT_24, temp_color);
            atk_md0280_show_string(90 + 4 * 24, 10, 24, 24, "C", ATK_MD0280_LCD_FONT_24, temp_color);
            last_temp_int = dth11_data.temperature_int;
            last_temp_dec = dth11_data.temperature_dec;
        }
        if (dth11_data.humidity_int != last_humi_int || dth11_data.humidity_dec != last_humi_dec) {
            atk_md0280_fill(90, 42, 90 + 5 * 24, 42 + 24, ATK_MD0280_WHITE);
            atk_md0280_show_num(90, 42, dth11_data.humidity_int, 2, ATK_MD0280_LCD_FONT_24, humi_color);
            atk_md0280_show_char(90 + 2 * 24, 42, '.', ATK_MD0280_LCD_FONT_24, humi_color);
            atk_md0280_show_num(90 + 3 * 24, 42, dth11_data.humidity_dec, 1, ATK_MD0280_LCD_FONT_24, humi_color);
            atk_md0280_show_string(90 + 4 * 24, 42, 24, 24, "%", ATK_MD0280_LCD_FONT_24, humi_color);
            last_humi_int = dth11_data.humidity_int;
            last_humi_dec = dth11_data.humidity_dec;
        }
        atk_md0280_show_string(10, 10, 80, 24, "temp:", ATK_MD0280_LCD_FONT_24, ATK_MD0280_BLACK);
        atk_md0280_show_string(10, 42, 80, 24, "humi:", ATK_MD0280_LCD_FONT_24, ATK_MD0280_BLACK);
        send_temp_humi_uart(temp, humi);
    }
}

#define PI (float)(3.1415926)

static float cube[8][3] = {
    {-16, -16, -16},
    {-16, +16, -16},
    {+16, +16, -16},
    {+16, -16, -16},
    {-16, -16, +16},
    {-16, +16, +16},
    {+16, +16, +16},
    {+16, -16, +16}
};

static uint8_t line_id[24] = {
    1, 2, 2, 3,
    3, 4, 4, 1,
    5, 6, 6, 7,
    7, 8, 8, 5,
    8, 4, 7, 3,
    6, 2, 5, 1
};

/**
 * @brief       计算矩阵乘法
 * @param       a      : 矩阵a
 *              b[3][3]: 矩阵b
 * @retval      计算结果
 */
static float *demo_matconv(float *a, float b[3][3])
{
    float res[3];
    uint8_t res_index;
    uint8_t a_index;
    
    for (res_index=0; res_index<3; res_index++)
    {
        res[res_index] = b[res_index][0] * a[0] + b[res_index][1] * a[1] + b[res_index][2] * a[2];
    }
    
    for (a_index=0; a_index<3; a_index++)
    {
        a[a_index] = res[a_index];
    }
    
    return a;
}

/**
 * @brief       旋转向量
 * @param       point: 需要旋转的向量
 *              x    : X轴旋转量
 *              y    : Y轴旋转量
 *              z    : Z轴旋转量
 * @retval      计算结果
 */
static void demo_rotate(float *point, float x, float y, float z)
{
    float rx[3][3];
    float ry[3][3];
    float rz[3][3];
    
    x /= PI;
    y /= PI;
    z /= PI;
    
    rx[0][0] = cos(x);
    rx[0][1] = 0;
    rx[0][2] = sin(x);
    rx[1][0] = 0;
    rx[1][1] = 1;
    rx[1][2] = 0;
    rx[2][0] = -sin(x);
    rx[2][1] = 0;
    rx[2][2] = cos(x);
    
    ry[0][0] = 1;
    ry[0][1] = 0;
    ry[0][2] = 0;
    ry[1][0] = 0;
    ry[1][1] = cos(y);
    ry[1][2] = -sin(y);
    ry[2][0] = 0;
    ry[2][1] = sin(y);
    ry[2][2] = cos(y);
    
    rz[0][0] = cos(z);
    rz[0][1] = -sin(z);
    rz[0][2] = 0;
    rz[1][0] = sin(z);
    rz[1][1] = cos(z);
    rz[1][2] = 0;
    rz[2][0] = 0;
    rz[2][1] = 0;
    rz[2][2] = 1;
    
    demo_matconv(demo_matconv(demo_matconv(point, rz), ry), rx);
}

// /**
//  * @brief       演示立方体3D旋转
//  * @param       无
//  * @retval      无
//  */
// static void demo_show_cube(void)
// {
//     uint8_t point_index;
//     uint8_t line_index;
//     static uint16_t x = 120;
//     static uint16_t y = 201;
    
//     for (point_index=0; point_index<8; point_index++)
//     {
//         demo_rotate(cube[point_index], 0.5f, 0.3f, 0.2f);
//     }
    
//     for (line_index=0; line_index<24; line_index+=2)
//     {
//         /* ATK-MD0280模块LCD画线段 */
//         atk_md0280_draw_line(   x + cube[line_id[line_index] - 1][0],
//                                 y + cube[line_id[line_index] - 1][1],
//                                 x + cube[line_id[line_index + 1] - 1][0],
//                                 y + cube[line_id[line_index + 1] - 1][1],
//                                 ATK_MD0280_BLUE);
//     }
//     tx_thread_sleep(100);
//     atk_md0280_fill(x - 28, y - 28, x + 28, y + 28, ATK_MD0280_WHITE);
// }

// 立方体区域优化：先用白线覆盖旧线，再画新线，或者降低刷新频率
static void demo_show_cube(void)
{
    static float last_cube[8][3];
    static uint8_t first = 1;
    uint8_t point_index, line_index;
    static uint16_t x = 120;
    static uint16_t y = 201;

    // 先覆盖旧线
    if (!first) {
        for (line_index=0; line_index<24; line_index+=2) {
            atk_md0280_draw_line(
                x + last_cube[line_id[line_index] - 1][0],
                y + last_cube[line_id[line_index] - 1][1],
                x + last_cube[line_id[line_index + 1] - 1][0],
                y + last_cube[line_id[line_index + 1] - 1][1],
                ATK_MD0280_WHITE);
        }
    }

    for (point_index=0; point_index<8; point_index++) {
        demo_rotate(cube[point_index], 0.5f, 0.3f, 0.2f);
        // 保存当前点
        last_cube[point_index][0] = cube[point_index][0];
        last_cube[point_index][1] = cube[point_index][1];
        last_cube[point_index][2] = cube[point_index][2];
    }

    // 画新线
    for (line_index=0; line_index<24; line_index+=2) {
        atk_md0280_draw_line(
            x + cube[line_id[line_index] - 1][0],
            y + cube[line_id[line_index] - 1][1],
            x + cube[line_id[line_index + 1] - 1][0],
            y + cube[line_id[line_index + 1] - 1][1],
            ATK_MD0280_BLUE);
    }
    first = 0;
    tx_thread_sleep(100);
}

static void show_horizontal_anim(void)
{
    static int x = 10;
    static int dir = 1;
    int y = 250; // 立方体下方的y坐标
    int width = 40;
    int height = 16;

    // 擦除上一次的小方块
    atk_md0280_fill(10, y, ATK_MD0280_LCD_WIDTH - 10, y + height, ATK_MD0280_WHITE);

    // 画当前的小方块
    atk_md0280_fill(x, y, x + width, y + height, ATK_MD0280_RED);

    // 更新位置
    x += dir * 4;
    if(x < 10) {
        x = 10;
        dir = 1;
    } else if(x + width > ATK_MD0280_LCD_WIDTH - 10) {
        x = ATK_MD0280_LCD_WIDTH - 10 - width;
        dir = -1;
    }
}
/* USER CODE END PFP */

/**
  * @brief  Define the initial system.
  * @param  first_unused_memory : Pointer to the first unused memory
  * @retval None
  */
VOID tx_application_define(VOID *first_unused_memory)
{
  /* USER CODE BEGIN  tx_application_define */
  (void)first_unused_memory;
  /* USER CODE END  tx_application_define */

  VOID *memory_ptr;

  if (tx_byte_pool_create(&tx_app_byte_pool, "Tx App memory pool", tx_byte_pool_buffer, TX_APP_MEM_POOL_SIZE) != TX_SUCCESS)
  {
    /* USER CODE BEGIN TX_Byte_Pool_Error */

    /* USER CODE END TX_Byte_Pool_Error */
  }
  else
  {
    /* USER CODE BEGIN TX_Byte_Pool_Success */

    /* USER CODE END TX_Byte_Pool_Success */

    memory_ptr = (VOID *)&tx_app_byte_pool;

    if (App_ThreadX_Init(memory_ptr) != TX_SUCCESS)
    {
      /* USER CODE BEGIN  App_ThreadX_Init_Error */

      /* USER CODE END  App_ThreadX_Init_Error */
    }

    /* USER CODE BEGIN  App_ThreadX_Init_Success */
    DWT_Init(168);
    if (elog_user_init() == ELOG_NO_ERR) 
    { elog_start();}
    SystemWatch_Init(&tx_app_byte_pool);
    atk_md0280_init();
    atk_md0280_clear(ATK_MD0280_WHITE);

    UART_Device_init_config com_cfg = {
        .huart = &huart1,
        .expected_len = 25,      
        .rx_buf = (uint8_t (*)[2])com_buf,
        .rx_buf_size = 30,
        .rx_mode = UART_MODE_DMA,
        .tx_mode = UART_MODE_DMA,
        .timeout = 1000,
        .rx_complete_cb = sbus_callback, 
        .cb_type = UART_CALLBACK_DIRECT,
        .event_flag = 0x01,
    };
    com = UART_Init(&com_cfg);

    // 用内存池分配线程栈
    CHAR *demo_stack;
    tx_byte_allocate(&tx_app_byte_pool, (VOID **)&demo_stack, 1024, TX_NO_WAIT);
    tx_thread_create(&demoTaskHandle, "demoTask", demo_Task, 0,demo_stack, 1024, 6, 6, TX_NO_TIME_SLICE, TX_AUTO_START);
  }
}

/* USER CODE BEGIN  0 */


static void demo_Task(ULONG thread_input)
{
    (void)thread_input;
    while(1) {
      show_temp_humi_on_md0280();
      demo_show_cube();
      show_horizontal_anim();
      tx_thread_sleep(1); // 休眠100ms
    }
}

void send_temp_humi_uart(float temp, float humi)
{
    uint16_t temp_i = (uint16_t)(temp * 100);
    uint16_t humi_i = (uint16_t)(humi * 100);
    static uint8_t buf[6];
    buf[0] = 0xAA;
    buf[1] = (temp_i >> 8) & 0xFF;
    buf[2] = temp_i & 0xFF;
    buf[3] = (humi_i >> 8) & 0xFF;
    buf[4] = humi_i & 0xFF;
    buf[5] = 0x5A;
    if(com!=NULL) UART_Send(com, buf, 6);
}

void sbus_callback(uint8_t *buf, uint16_t len){
    (void)buf; // 避免未使用参数警告
    (void)len; // 避免未使用参数警告
}
/* USER CODE END  0 */
